# 3.1什么是可散列的数据类型
# 1.如果一个对象是可散列的，那么它的散列值是不变的，
# 而且这个对象需要实现hash方法，得到hash值
# 2.不可变数据类型（str,bytes和数值类型）都是
# 可散列类型， frozenset里只能容纳可散列类型，
# 元组的话，只有当一个元组包含的所有元素都是
# 可散列类型的情况下，它才是可散列的

# 实例：tt,tl,tf
1.
# tt = (1, 2, (30, 40))
# print(hash(tt))
2.
# tl = (1, 2, [30, 40])
# print(hash(tl))




# 3.2字典推导
# 字典推导可以从任何以键值对作为元素的可迭代对象
# 中构建出字典
# 实例：拨号代码
1.
# dial_codes = [
#     (86, 'china'),
#     (91, 'india'),
#     (1, 'united states'),
#     (62, 'indonesia'),
#     (55, 'brazil'),
#     (92, 'pakistan')
# ]
#以键值对作为元素的可迭代对象构建字典
# country_code = {country: code for code, country in dial_codes}
# print(country_code)
#
#因为country_code已经是字典了，所以要用items,
# 把键与值位置调换，并且把国家所有字母大写
# print({code: country.upper() for country, code in country_code.items()})

# 3.3常见的映射方法
# 用setdefault处理找不到的键
# 如果键不存在，把键和一个空列表放进映射，然后返回
# 这个空列表，这样就能在不进行第二次查找的情况下更
# 新列表了
# my_dict = {'a': '1', 'b': '2', 'c': '3'}
# my_dict.setdefault('d', []).append(4)
# print(my_dict)

# 二者的效果是一样的，只不过后者至少要进行两次键查询
# 如果键不存在的话，就是三次，用 setdefault 只需要
# 一次就可以完成整个操作
# if 'd' not in my_dict:
#     my_dict['d'] = []
# my_dict['d'].append(4)
# print(my_dict)



# 3.4.1　 defaultdict ：处理找不到的键的一个选择
# 在用户创建 defaultdict 对象的时候，就需要给它配置
# 一个为找不到的键创造默认值的方法。
# 比如，我们新建了这样一个字典： dd = defaultdict(list) ，
# 如果键 'new-key' 在 dd 中还不存在的话，
# 表达式 dd['new-key'] 会按照以下的步骤来行事。
# (1) 调用 list() 来建立一个新列表。
# (2) 把这个新列表作为值， 'new-key' 作为它的键，
# 放到 dd 中。
# (3) 返回这个列表的引用。
# from collections import defaultdict
# dd = defaultdict(list)
# 可以看到，字典是空的，值是列表类型
# print(dd)
# 当Apple键不在字典dd里面的时候，就会实例化一个值作为
# 默认值
# print(dd['apple'])
# print(dd)




# 3.4.2 __missing__方法
# 当使用__getitem__方法访问一个不存在的键时，dict[key]
# 这种形式是__getitem__方法的简化形式，会调用Misssing方法.
# 稍加修改，获取默认值，并将该值添加到字典中
# 定义一个子类：

# class Missing(dict):
#     def __missing__(self, key):
#         self[key] = 'default'
#         return 'missing'
# d = Missing()
# 可以看到，d是一个空的字典
# print(d)
# apple键在字典中不存在，调用了misssing方法
# print(d['apple'])
# 设置的默认值default添加到字典中
# print(d)


# 3.5字典的变种
# 1.collections.Counter
# 这个映射类型会给键准备一个整数计数器。每次更新
# 一个键的时候都会增加这个计数器
import collections
# ct = collections.Counter('aabbccddd')
# 字母作为键，计数作为值
# print(ct)
# ct.update('aaaabbbb')
# print(ct)
#  most_common([n]) 会按照次序返回映射里最
# 常见的 n 个键和它们的计数
# print(ct.most_common(2))



# 2.collections.OrderedDict
# 这个类型在添加键的时候会保持顺序，因此
# 键的迭代次序总是一致的。 OrderedDict的
# popitem 方法默认删除并返回的是字典里的最后
# 一个元素，但是如果像 my_odict.popitem(last=False)
# 这样调用它，那么它删除并返回第一个被添加进去的元素。

# 3.collections.ChainMap
# 该类型可以容纳数个不同的映射对象，然后在进行
# 键查找操作的时候，这些对象会被当作一个
# 整体被逐个查找，直到键被找到为止



# 3.6子类化 UserDict
# 以 UserDict 为基类，总比以普通的 dict 为基类
# 要来得方便。更倾向于 UserDict 而不是从 dict 继承
# 的主要原因是，后者有时会在某些方法的实现上
# 走一些捷径，导致我们不得不在它的子类中重写这些方法，
# 但是 UserDict 就不会带来这些问题。














